(function($) {
	var oldLoadDataMethod = $.fn.datagrid.methods.loadData;
	$.fn.datagrid.methods.loadData = function(jq, data) {
		jq.each(function() {
			$.data(this, 'datagrid').filterSource = null;
		});
		return oldLoadDataMethod.call($.fn.datagrid.methods, jq, data);
	};

	$.extend($.fn.datagrid.defaults, {
		filterBtnIconCls : 'icon-filter',
		filterMenuIconCls : 'icon-ok',
		remoteFilter : false,
		filterDelay : 400,
		filterRules : [],
		filterStringify : function(data) {
			return JSON.stringify(data);
		}
	});

	// filter types
	$.fn.datagrid.defaults.filters = $.extend({}, $.fn.datagrid.defaults.editors, {
		label : {
			init : function(container, options) {
				return $('<span></span>').appendTo(container);
			},
			getValue : function(target) {
				return $(target).html();
			},
			setValue : function(target, value) {
				$(target).html(value);
			},
			resize : function(target, width) {
				$(target)._outerWidth(width)._outerHeight(22);
			}
		}
	});

	// filter operators
	$.fn.datagrid.defaults.operators = {
		nofilter : {
			text : 'No Filter'
		},
		contains : {
			text : 'Contains',
			isMatch : function(source, value) {
				source = source + ''; // 原始方法如过在字段为数字类型的时候会报错,这里将数据变为字符类型
				value = getTime(value); // 解决日期过滤问题
				return source.toLowerCase().indexOf(value.toLowerCase()) >= 0;
			}
		},
		equal : {
			text : 'Equal',
			isMatch : function(source, value) {
				value = getTime(value); // 解决日期过滤问题
				return source == value;
			}
		},
		notequal : {
			text : 'Not Equal',
			isMatch : function(source, value) {
				value = getTime(value); // 解决日期过滤问题
				return source != value;
			}
		},
		beginwith : {
			text : 'Begin With',
			isMatch : function(source, value) {
				source = source + ''; // 原始方法如过在字段为数字类型的时候会报错,这里将数据变为字符类型
				value = getTime(value); // 解决日期过滤问题
				return source.toLowerCase().indexOf(value.toLowerCase()) == 0;
			}
		},
		endwith : {
			text : 'End With',
			isMatch : function(source, value) {
				source = source + ''; // 原始方法如过在字段为数字类型的时候会报错,这里将数据变为字符类型
				value = getTime(value); // 解决日期过滤问题
				return source.toLowerCase().indexOf(value.toLowerCase(), source.length - value.length) !== -1;
			}
		},
		less : {
			text : 'Less',
			isMatch : function(source, value) {
				value = getTime(value); // 解决日期过滤问题
				return source < value;
			}
		},
		lessorequal : {
			text : 'Less Or Equal',
			isMatch : function(source, value) {
				value = getTime(value); // 解决日期过滤问题
				return source <= value;
			}
		},
		greater : {
			text : 'Greater',
			isMatch : function(source, value) {
				value = getTime(value); // 解决日期过滤问题
				return source > value;
			}
		},
		greaterorequal : {
			text : 'Greater Or Equal',
			isMatch : function(source, value) {
				value = getTime(value); // 解决日期过滤问题
				return source >= value;
			}
		}
	};

	/**
	 * 解决日期格式的数据过滤问题
	 */
	function getTime(v) {
		if (/^(\d{4})\-(\d{2})\-(\d{2})$/.test(v)) {
			return Date.parse(v);
		}
		return v;
	}

	function resizeFilter(target, field, width) {
		var dg = $(target);
		var header = dg.datagrid('getPanel').find('div.datagrid-header');
		var ff = field ? header.find('input.datagrid-filter[name="' + field + '"]') : header.find('input.datagrid-filter');
		ff.each(function() {
			var name = $(this).attr('name');
			var col = dg.datagrid('getColumnOption', name);
			var btn = $(this).closest('div.datagrid-filter-c').find('a.datagrid-filter-btn');
			if (width != undefined) {
				this.filter.resize(this, width);
			} else {
				this.filter.resize(this, col.width - btn._outerWidth());
			}
		});
	}

	function getFilterComponent(target, field) {
		var opts = $(target).datagrid('options');
		var header = $(target).datagrid('getPanel').find('div.datagrid-header');
		return header.find('tr.datagrid-filter-row td[field="' + field + '"] input.datagrid-filter');
	}

	/**
	 * get filter rule index, return -1 if not found.
	 */
	function getRuleIndex(target, field) {
		var rules = $(target).datagrid('options').filterRules;
		for (var i = 0; i < rules.length; i++) {
			if (rules[i].field == field) {
				return i;
			}
		}
		return -1;
	}

	function addFilterRule(target, param) {
		var opts = $(target).datagrid('options');
		var rules = opts.filterRules;
		var index = getRuleIndex(target, param.field);
		if (index >= 0) {
			if (param.op == 'nofilter') {
				removeFilterRule(target, param.field);
			} else {
				$.extend(rules[index], param);
			}
		} else {
			rules.push(param);
		}

		var input = getFilterComponent(target, param.field);
		if (input.length) {
			if (param.op != 'nofilter') {
				input[0].filter.setValue(input, param.value);
			}
			var menu = input[0].menu;
			if (menu) {
				menu.find('.' + opts.filterMenuIconCls).removeClass(opts.filterMenuIconCls);
				var item = menu.menu('findItem', opts.operators[param.op]['text']);
				menu.menu('setIcon', {
					target : item.target,
					iconCls : opts.filterMenuIconCls
				});
			}
		}
	}

	function removeFilterRule(target, field) {
		var dg = $(target);
		var opts = dg.datagrid('options');
		if (field) {
			var index = getRuleIndex(target, field);
			if (index >= 0) {
				opts.filterRules.splice(index, 1);
			}
			_clear([ field ]);
		} else {
			opts.filterRules = [];
			var fields = dg.datagrid('getColumnFields', true).concat(dg.datagrid('getColumnFields'));
			_clear(fields);
		}

		function _clear(fields) {
			for (var i = 0; i < fields.length; i++) {
				var input = getFilterComponent(target, fields[i]);
				if (input.length) {
					input[0].filter.setValue(input, '');
					var menu = input[0].menu;
					if (menu) {
						menu.find('.' + opts.filterMenuIconCls).removeClass(opts.filterMenuIconCls);
					}
				}
			}
		}
	}

	function doFilter(target) {
		var state = $.data(target, 'datagrid');
		var opts = state.options;
		if (opts.remoteFilter) {
			$(target).datagrid('load');
		} else {
			$(target).datagrid('loadData', state.filterSource || state.data);
		}
	}

	function myLoadFilter(data) {
		var state = $.data(this, 'datagrid');
		var opts = state.options;

		if ($.isArray(data)) {
			data = {
				total : data.length,
				rows : data
			};
		}
		if (!opts.remoteFilter && opts.filterRules.length) {
			if (!state.filterSource) {
				state.filterSource = data;
			}
			data = state.filterSource;
			var rows = [];
			for (var i = 0; i < data.rows.length; i++) {
				var row = data.rows[i];
				if (isMatch(row)) {
					rows.push(row);
				}
			}
			data = {
				total : data.total - (data.rows.length - rows.length),
				rows : rows
			};
		}
		return data;

		function isMatch(row) {
			var rules = opts.filterRules;
			for (var i = 0; i < rules.length; i++) {
				var rule = rules[i];
				var source = row[rule.field];
				if (source) {
					var op = opts.operators[rule.op];
					if (!op.isMatch(source, rule.value)) {
						return false
					}
				}
			}
			return true;
		}
	}

	function init(target, filters) {
		filters = filters || [];
		var state = $.data(target, 'datagrid');
		var opts = state.options;

		var onResizeColumn = opts.onResizeColumn;
		opts.onResizeColumn = function(field, width) {
			onResizeColumn.call(target, field, width);
			if (opts.fitColumns) {
				resizeFilter(target, null, 3)
				$(target).datagrid('fitColumns');
				resizeFilter(target)
			} else {
				resizeFilter(target, field);
			}
		};
		var onBeforeLoad = opts.onBeforeLoad;
		opts.onBeforeLoad = function(param) {
			param.filterRules = opts.filterStringify(opts.filterRules);
			return onBeforeLoad.call(this, param);
		};
		opts.loadFilter = myLoadFilter;

		initCss();
		createFilter(true);
		createFilter();
		if (opts.fitColumns) {
			setTimeout(function() {
				resizeFilter(target);
			}, 0);
		}

		function initCss() {
			if (!$('#datagrid-filter-style').length) {
				$('head')
						.append(
								'<style id="datagrid-filter-style">'
										+ 'a.datagrid-filter-btn{display:inline-block;width:22px;height:22px;vertical-align:top;cursor:pointer;opacity:0.6;filter:alpha(opacity=60);}'
										+ 'a:hover.datagrid-filter-btn{opacity:1;filter:alpha(opacity=100);}' + '</style>');
			}
		}

		/**
		 * create filter component
		 */
		function createFilter(frozen) {
			var dc = state.dc;
			var fields = $(target).datagrid('getColumnFields', frozen);
			if (frozen && opts.rownumbers) {
				fields.unshift('_');
			}
			var table = (frozen ? dc.header1 : dc.header2).find('table.datagrid-htable');
			table.find('tr').each(function() {
				$(this).height($(this).height());
			});

			// clear the old filter component
			table.find('input.datagrid-filter').each(function() {
				if (this.filter.destroy) {
					this.filter.destroy(this);
				}
				if (this.menu) {
					$(this.menu).menu('destroy');
				}
			});
			table.find('tr.datagrid-filter-row').remove();

			var tr = $('<tr class="datagrid-header-row datagrid-filter-row"></tr>').appendTo(table.find('tbody'));

			for (var i = 0; i < fields.length; i++) {
				var field = fields[i];
				var col = $(target).datagrid('getColumnOption', field);
				if (col && (col.checkbox || col.expander)) {
					field = '_';
				}
				var td = $('<td></td>').attr('field', field).appendTo(tr);
				if (field == '_') {
					continue;
				}
				var div = $('<div class="datagrid-filter-c"></div>').appendTo(td);

				var fopts = getFilter(field);
				var type = fopts ? fopts.type : 'text';

				var filter = opts.filters[fopts ? fopts.type : 'text'];
				var input = filter.init(div, fopts ? (fopts.options || {}) : {});
				input.addClass('datagrid-filter').attr('name', field);
				input[0].filter = filter;

				if (fopts) {
					input[0].menu = createFilterButton(div, fopts.op);
					if (fopts.options && fopts.options.onInit) {
						fopts.options.onInit.call(input[0]);
					}
				} else if (type == 'text') {
					input.bind('keydown', function(e) {
						var t = $(this);
						if (this.timer) {
							clearTimeout(this.timer);
						}
						if (e.keyCode == 13) {
							addFilterRule(target, {
								field : t.attr('name'),
								op : 'contains',
								value : t.val()
							});
							doFilter(target);
						} else {
							this.timer = setTimeout(function() {
								addFilterRule(target, {
									field : t.attr('name'),
									op : 'contains',
									value : t.val()
								});
								doFilter(target);
							}, opts.filterDelay);
						}
					});
				}

				resizeFilter(target, field);
			}
		}

		function createFilterButton(container, operators) {
			if (!operators) {
				return null;
			}

			var btn = $('<a class="datagrid-filter-btn">&nbsp;</a>').addClass(opts.filterBtnIconCls).appendTo(container);
			var menu = $('<div></div>').appendTo('body');
			menu.menu({
				alignTo : btn,
				onClick : function(item) {
					var btn = $(this).menu('options').alignTo;
					var td = btn.closest('td[field]');
					var field = td.attr('field');
					var input = td.find('input.datagrid-filter');
					var value = input[0].filter.getValue(input);

					addFilterRule(target, {
						field : field,
						op : item.name,
						value : value
					});

					doFilter(target);
				}
			});
			$.each([ 'nofilter' ].concat(operators), function(index, item) {
				var op = opts.operators[item];
				if (op) {
					menu.menu('appendItem', {
						text : op.text,
						name : item
					});
				}
			});
			btn.bind('click', {
				menu : menu
			}, function(e) {
				$(e.data.menu).menu('show');
				return false;
			});
			return menu;
		}

		function getFilter(field) {
			for (var i = 0; i < filters.length; i++) {
				var filter = filters[i];
				if (filter.field == field) {
					return filter;
				}
			}
			return null;
		}
	}

	$.extend($.fn.datagrid.methods, {
		enableFilter : function(jq, filters) {
			return jq.each(function() {
				init(this, filters);
			});
		},
		addFilterRule : function(jq, param) {
			return jq.each(function() {
				addFilterRule(this, param);
			});
		},
		removeFilterRule : function(jq, field) {
			return jq.each(function() {
				removeFilterRule(this, field);
			});
		},
		doFilter : function(jq) {
			return jq.each(function() {
				doFilter(this);
			});
		},
		getFilterComponent : function(jq, field) {
			return getFilterComponent(jq[0], field);
		}
	});
})(jQuery);
